//#include<bits/stdc++.h>
//using namespace std;
//#define int long long
//const int MAXN = 2e3 + 10;
//const int MAXM = 6e3 + 10;
//int n, m;
//struct Edge {
//	int to, w, next;
//} edge[MAXM]; // 链式前向星存图
//int head[MAXN], tot;
//inline void Init()
//{
//	for (int i = 0; i < MAXM; i++) {
//		edge[i].next = 0;
//	}
//	for (int i = 0; i < MAXN; i++) {
//		head[i] = 0;
//	}
//	tot = 0;
//}
//inline void addedge(int u, int v, int w)
//{
//	edge[++tot].to = w;
//	edge[tot].w = w;
//	edge[tot].next = head[u];
//	head[u] = tot;
//}
//queue<int> q;
//int dis[MAXN], vis[MAXN], cnt[MAXN];
//bool spfa()
//{
//	while(!q.empty()) q.pop();
//	memset(dis, 0x3f, sizeof(dis));
//	memset(vis, 0, sizeof(vis));
//	memset(cnt, 0, sizeof(cnt));
//	dis[1] = 0;
//	vis[1] = true;
//	q.push(1);
//	while (!q.empty()) {
//		int x = q.front();
//		q.pop();
//		vis[x] = false;
//		for (int i = head[x]; i; i = edge[i].next) {
//			int y = edge[i].to, z = edge[i].w;
//			if (dis[y] > dis[x] + z) {
//				dis[y] = dis[x] + z;// 更新最短路
//				cnt[y] = cnt[x] + 1;// 更新包含边数
//				if (cnt[y] >= n) {// WA 忘记等号
//					return true;
//				}
//				if (! vis[y]) {
//					q.push(y);
//					vis[y] = true;
//				}
//			}
//		}
//	}
//	return false;
//}
//signed main()
//{
//	int T;
//	cin >> T;
//	while (T --) {
//		Init();
//
//		cin >> n >> m;
//		for (int i = 1; i <= m; i++) {
//			int u, v, w;
//			cin >> u >> v >> w;
//			addedge(u, v, w);
//			if (w >= 0) {
//				addedge(v, u, w);
//			}
//		}
//
//		if (spfa()) {
//			cout << "YES";
//		} else {
//			cout << "NO";
//		}
//	}
//	return 0;
//}
//#include <bits/stdc++.h>
//using namespace std;
//
//const int INF = INT_MAX;
//
//struct Edge {
//	int u, v, w;
//};
//
//bool findNegativeCycle(int n, vector<Edge>& edges)
//{
//	vector<int> dist(n + 1, INF); // 距离数组，初始化为无穷大
//	dist[1] = 0; // 顶点1的距离初始化为0
//
//	// 进行n-1次松弛操作
//	for (int i = 1; i <= n - 1; ++i) {
//		// 遍历所有边进行松弛操作
//		for (const auto& edge : edges) {
//			int u = edge.u;
//			int v = edge.v;
//			int w = edge.w;
//
//			// 如果能通过当前边更新顶点的最短路径距离，则进行更新
//			if (dist[u] != INF && dist[u] + w < dist[v]) {
//				dist[v] = dist[u] + w;
//			}
//		}
//	}
//
//	// 再进行一次松弛操作，如果存在距离更新，则存在负环
//	for (const auto& edge : edges) {
//		int u = edge.u;
//		int v = edge.v;
//		int w = edge.w;
//
//		if (dist[u] != INF && dist[u] + w < dist[v]) {
//			return true; // 存在负环
//		}
//	}
//
//	return false; // 不存在负环
//}
//
//int main()
//{
////	输入
//	int T;
//	cin >> T;
//
//	while (T--) {
//		int n, m;
//		cin >> n >> m;
//
//		vector<Edge> edges(m);
//		for (int i = 0; i < m; ++i) {
//			cin >> edges[i].u >> edges[i].v >> edges[i].w;
//		}
//		//计算
//		if (findNegativeCycle(n, edges)) {
//			cout << "YES" << endl;
//		} else {
//			cout << "NO" << endl;
//		}
//	}
//
//	return 0;
//}//WA
#include<cstdio>
#include<cstring>
#include<queue>
#define inf 0x3f3f3f3f
using namespace std;
const int MAXN = 2010;
const int MAXM = 3010;
int n, m;
// 将头指针数组设置为-1且边的数量为0
int en = -1, eh[MAXN];
struct edge {
	int u, v, w, next;
	edge(int U = 0, int V = 0, int W = 0, int N = 0): u(U), v(V), w(W), next(N) {}
};
// 因此，e 数组大小为 MAXM 的两倍，能够存储 MAXM 条边的信息。
edge e[MAXM << 1]; // 存储边的结构体数组
inline void add_edge(int u, int v, int w)
{
	e[++en] = edge(u, v, w, eh[u]);
	eh[u] = en; // 将边加入邻接表结构中
}
void input()
{
	scanf("%d %d", &n, &m);
	en = -1;
	memset(eh, -1, sizeof(eh));
	int u, v, w;
	for (int i = 1; i <= m; ++i) {
		scanf("%d %d %d", &u, &v, &w);
		add_edge(u, v, w); // 添加有向边
		if (w >= 0)add_edge(v, u, w); // 如果边权为非负，则添加反向边
	}
}

int dis[MAXN], cnt[MAXN];
bool vis[MAXN];
queue<int> q;
void spfa()
{
	fill(dis + 1, dis + n + 1, inf); // 将距离数组初始化为无穷大
	memset(cnt, 0, sizeof(cnt));
	memset(vis, 0, sizeof(vis));

	while (!q.empty())q.pop();// 清空 初始化
	dis[1] = 0;
	vis[1] = 1;
	q.push(1); // 初始点1入队，并更新距离为0

	int u, v, w;
	while (!q.empty()) {
		u = q.front();
		vis[u] = 0;
		q.pop();
		for (int i = eh[u]; i != -1; i = e[i].next) { // 遍历以点u为起点的所有边
			v = e[i].v;
			w = e[i].w;
			if (dis[u] + w < dis[v]) { // 如果通过边u->v可以松弛距离
				dis[v] = dis[u] + w; // 更新距离
				if (!vis[v]) {
					if (++cnt[v] >= n) { // 如果顶点v入队次数达到n，则存在负环
						printf("YES\n");
						return;
					}
					vis[v] = 1;
					q.push(v); // 将顶点v入队
				}
			}
		}
	}
	printf("NO\n"); // 不存在负环
}

int main()
{
	int t;
	scanf("%d", &t);
	for (int i = 1; i <= t; ++i) {
		input(); // 输入函数
		spfa(); // 运行SPFA算法判断是否存在负环
	}
	return 0;
}
